'use strict';
let nodes = new function() {
	cvs.addEventListener('dblclick', e => cvs.webkitRequestFullScreen());
	
	class Child extends EventEmitter {
		constructor() {
			super();
			this._parent = null;
			this._children = [];
		}
		appendChild(child) {
			if(child._parent) child._parent.removeChild(child);
			child._parent = this;
			this._children.push(child);
		}
		removeChild(child) {
			if(child instanceof Child) {
				let l = this._children.indexOf(child);
				if(~l) this._children.splice(l, 1)[0]._parent = null;
			};
		}
		getChildren() { return [...this._children]; };
		getChainParent() {
			let arr = [];
			let pr = this._parent;
			for(let i = 0; pr && i < 100; i++) {
				arr.push(pr);
				pr = pr._parent;
			};
			return arr;
		}
	};
	
	
	let DataNode = this.DataNode = class extends EventEmitter {
		constructor(data = null) {
			super();
			this.data = new Value(data);
			
			this._stdin = new Value('');
			this._stdout = new Value('');
			this._stdout.lock(true);
			
			// канал	stream
			// поток	flow
			// pip
			// port
			// input data stream
			// stdin | standard input / output stream
		}
		set stdin(v) {
			if(v instanceof Value) this._stdin.new = v;
			else if(typeof v === 'function') this._stdin.new = v;
			else this._stdin.new = v;
		}
		get stdout() { return this._stdout; }
	};
	setPropertyNotEnumerable(DataNode.prototype, Symbol.toStringTag, 'DataNode');
	
	
	
	
	let TaskNode = this.TaskNode = class {
		constructor(p = {}) {
			this.title = new Value(p.title||'Task');
			this.description = new Value(p.description||'Task description');
			this.done = false;
		}
	};
	
	
	
	
	let TaskListNode = this.TaskListNode = class {
		constructor() {
			this.tasks = [];
		}
		get size() { return this.tasks.length; }
		
		add(...args) { return this.tasks.push(...args); }
		remove(...args) {
			let removed = 0;
			for(let i = 0; i < args.length; i++) {
				let l = -1;
				if(args[i] instanceof TaskNode) l = this.tasks.indexOf(args[i]);
				else if('number' === typeof args[i]) l = args[i];
				if(~l-removed) this.tasks.splice(l-removed++, 1);
			};
		}
		// TODO: добавить getter по полям
		get(i) { return this.tasks[i]; }
		// TODO: незабыть убрать
		log() { return this.tasks.map(i => `- [${i.done ? 'x' : ' '}] ${i.title}:\n${i.description}`); }
	};
	
	
	function drawPanel(ctx, pos, size, fill = '#203040', stroke = '#338899', line = 1) {
		ctx.beginPath();
		ctx.lineWidth = line;
		ctx.fillStyle = fill;
		ctx.strokeStyle = stroke;
		ctx.fillRect(pos.x, pos.y, size.x, size.y);
		ctx.strokeRect(pos.x, pos.y, size.x, size.y);
	};
	
	function drawTaskNode(ctx, pos, task, color, fontSize, fontSizeTitle) {
		fontSizeTitle = fontSizeTitle||fontSize;
		ctx.beginPath();
		
		ctx.fillStyle = task.done ? '#eeeeee' : '#77ee77';
		ctx.font = fontSizeTitle+'px monospace';
		ctx.fillText(`- [${task.done ? 'x':'o'}] ${task.title}`, pos.x, pos.y);
		
		ctx.fillStyle = color;
		ctx.font = fontSize+'px monospace';
		ctx.fillText(task.description, pos.x, pos.y + fontSizeTitle*1.2);
	};
	
	
	let PanelViewFactory = this.PanelViewFactory = new function() {
		this.create = function(type, p) {
			if(type === 'task') return new this.TaskNode(p);
			if(type === 'list') return new this.TaskListNode(p);
			if(type === 'value') return new this.Value(p);
			return new Value('');
		};
	
	
		this.BaseNode = class extends Child {
			constructor(p = {}) {
				super();
				this.pos = p.pos||vec2();
				this.size = p.size||vec2(210, 120);
				this.scale = p.scele||vec2(1, 1);
				
				this.padding = p.padding||0;
				this.color = p.color||'#eeeeee';
				this.fontSize = p.fontSize||10;
				this.background = p.background||'#203040';
				this.borderColor = p.barderColor||'#338899';
				this.borderWidth = p.borderWidth||1;
			}
			get globalPos() {
				let v = vec2(this.pos);
				let tParent = this.getChainParent();
				for(let i = 0; i < tParent.length; i++) v.plus(tParent[i].pos);
				return v;
			}
		};
		
		
		this.Value = class extends this.BaseNode {
			constructor(p = {}) {
				super(p);
				
				this.data = new Value();
			}
			draw(ctx) {
				let fillSize = this.size.buf().inc(this.scale);
				
				let pad = vec2(this.padding, this.padding); // this.pos.buf(Math.min(5, this.size.x/100*5), Math.min(5, this.size.y/100*5));
				let pos = this.globalPos;
				
				ctx.save();
				drawPanel(ctx, pos, fillSize, '#444400', '#eeff00', 0.5);
				pos = pos.buf(pad);
				
				drawPanel(ctx, pos, vec2(fillSize.x - this.padding*2, this.fontSize*2.2), '#444400', '#eeff00', 0.5);
				
				ctx.beginPath();
				ctx.textBaseline = 'top';
				ctx.fillStyle = this.color;
				ctx.font = this.fontSize+'px monospace';
				ctx.fillText(this.data.value, pos.x, pos.y);
				
				ctx.restore();
			}
		};
		
		
		this.TaskNode = class extends this.BaseNode {
			constructor(p = {}) {
				super(p);
				this.data = new TaskNode();
			}
			draw(ctx) {
				let fillSize = this.size.buf().inc(this.scale);
				
				let pad = vec2(this.padding, this.padding); // this.pos.buf(Math.min(5, this.size.x/100*5), Math.min(5, this.size.y/100*5));
				let pos = this.globalPos;
				
				let task = this.data;
				let fontSizeTitle = this.fontSize*1.2;
				
				ctx.save();
				drawPanel(ctx, pos, fillSize, '#444400', '#eeff00', 0.5);
				pos = pos.buf(pad);
				
				ctx.beginPath();
				ctx.fillStyle = this.color;
				ctx.font = this.fontSize+'px monospace';
				ctx.textBaseline = 'top';
				
				drawTaskNode(ctx, pos, task, this.color, this.fontSize, this.fontSize*1.2);
			}
		};
		
		
		this.TaskListNode = class extends this.BaseNode {
			constructor(p = {}) {
				super(p);
				this.data = new TaskListNode();
			}
			addTask(p = {}) {
				let task = new TaskNode(p);
				this.appendChild(task);
				return this.data.add(task);
			}
			draw(ctx) {
				let fillSize = this.size.buf().inc(this.scale);
				
				let pad = vec2(this.padding, this.padding); // this.pos.buf(Math.min(5, this.size.x/100*5), Math.min(5, this.size.y/100*5));
				let pos = this.globalPos;
					
				let height = this.fontSize*2.2;
				let gap = 5;
				
				ctx.save();
				drawPanel(ctx, this.pos, fillSize, '#444400', '#eeff00', 0.5);
				
				for(let i = 0; i < this.data.size; i++) {
					let task = this.data.get(i);
					
					pos = pos.buf(pad);
					
					let tParent = this.getChainParent();
					for(let i = 0; i < tParent.length; i++) {
						pos.plus(tParent[i].pos);
					};
					
					pos.plus(0, (height+gap) * i);
					drawPanel(ctx, pos, vec2(fillSize.x - this.padding*2, height), '#444400', '#eeff00', 0.5);
					drawTaskNode(ctx, pos, task, this.color, this.fontSize, this.fontSize*1.2);
				};
			}
		};
	};
};